import { redisClient } from '../redis/client';
import { sign, verify } from 'jsonwebtoken';
import { UserPayload } from '../types/auth';
import { ApiError } from '../utils/ApiError';
import { StatusCodes } from 'http-status-codes';

const TOKEN_EXPIRY = '1d';
const REFRESH_TOKEN_EXPIRY = '7d';
const MAX_LOGIN_ATTEMPTS = 5;
const LOCKOUT_TIME = 15 * 60; // 15分钟锁定时间（秒）

export class SecurityService {
  private static instance: SecurityService;
  
  private constructor() {}

  public static getInstance(): SecurityService {
    if (!SecurityService.instance) {
      SecurityService.instance = new SecurityService();
    }
    return SecurityService.instance;
  }

  public async generateTokens(payload: Omit<UserPayload, 'tokens'>): Promise<{ token: string; refreshToken: string }> {
    if (!process.env.JWT_SECRET || process.env.JWT_SECRET.length < 32) {
      throw new ApiError(StatusCodes.INTERNAL_SERVER_ERROR, 'Invalid JWT configuration');
    }

    const tokenPayload = {
      id: payload.id,
      email: payload.email,
      role: payload.role
    };

    const token = sign(tokenPayload, process.env.JWT_SECRET, {
      expiresIn: TOKEN_EXPIRY,
      algorithm: 'HS256'
    });

    const refreshToken = sign(tokenPayload, process.env.JWT_SECRET, {
      expiresIn: REFRESH_TOKEN_EXPIRY,
      algorithm: 'HS256'
    });

    return { token, refreshToken };
  }

  public verifyToken(token: string): { id: number; email: string; role: string } {
    if (!process.env.JWT_SECRET) {
      throw new ApiError(StatusCodes.INTERNAL_SERVER_ERROR, 'JWT secret not configured');
    }

    try {
      return verify(token, process.env.JWT_SECRET) as { id: number; email: string; role: string };
    } catch (error) {
      throw new ApiError(StatusCodes.UNAUTHORIZED, 'Invalid token');
    }
  }

  public async getLoginAttempts(email: string): Promise<number> {
    const key = `login_attempts:${email}`;
    const attempts = await redisClient.get(key);
    return attempts ? parseInt(attempts, 10) : 0;
  }

  public async incrementLoginAttempts(email: string): Promise<void> {
    const key = `login_attempts:${email}`;
    await redisClient.incr(key);
    await redisClient.expire(key, LOCKOUT_TIME);
  }

  public async resetLoginAttempts(email: string): Promise<void> {
    const key = `login_attempts:${email}`;
    await redisClient.del(key);
  }

  public async isAccountLocked(email: string): Promise<boolean> {
    const attempts = await this.getLoginAttempts(email);
    return attempts >= MAX_LOGIN_ATTEMPTS;
  }

  public async getLockoutTimeRemaining(email: string): Promise<number> {
    const key = `login_attempts:${email}`;
    return redisClient.ttl(key);
  }
}

export const securityService = SecurityService.getInstance();

export default securityService;
